feat(tbor): implement ECC keygen, sign, and ECDH derive - #585
Conversation
0db7106 to
b3e53f6
Compare
b3e53f6 to
427588b
Compare
ef86362 to
f3eaaf9
Compare
427588b to
db29d52
Compare
f3eaaf9 to
46b9a59
Compare
db29d52 to
3655e3b
Compare
46b9a59 to
b6570db
Compare
3655e3b to
2e032a4
Compare
b6570db to
2e8344f
Compare
2e032a4 to
c4bd905
Compare
2e8344f to
9ac2c4f
Compare
There was a problem hiding this comment.
Pull request overview
This PR adds three new in-session, stateless masked-key TBOR crypto commands to the firmware and host-side TBOR type layers: ECC key generation (0x17), ECDSA signing (0x18), and ECDH derivation (0x19). The intent is to mirror existing MBOR functionality while returning caller-held masked blobs (no on-device persistence) and providing emulator integration coverage and documentation.
Changes:
- Add firmware TBOR handlers for
EccGenerateKey,EccSign, andEcdhDerive, plus opcode wiring/classification. - Add shared PAL↔wire mappings (
ecc_private_curve,ecc_private,ecdh_secret) and new FW/host TBOR wire schemas. - Add emulator integration tests and TBOR-DDI documentation for the three new commands.
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| fw/core/lib/src/op.rs | Classifies the new opcodes as InSession for session-flag validation. |
| fw/core/lib/src/ddi/tbor/mod.rs | Declares new modules; defines opcodes 0x17..=0x19; wires dispatch + opcode classifiers. |
| fw/core/lib/src/ddi/tbor/from_pal.rs | Adds strict ECC-private curve recovery plus curve→kind mappings for masked ECC keys and ECDH secrets. |
| fw/core/lib/src/ddi/tbor/ecc_generate_key.rs | Implements ECC keypair generation and masking of the private key into the response. |
| fw/core/lib/src/ddi/tbor/ecc_sign.rs | Implements masked-key ECDSA signing with in-place unmasking and signature response. |
| fw/core/lib/src/ddi/tbor/ecdh_derive.rs | Implements masked-key ECDH derivation and re-masking of the derived secret. |
| fw/core/ddi/tbor/types/src/lib.rs | Registers the new FW-side TBOR schemas for ECC keygen/sign/ECDH. |
| fw/core/ddi/tbor/types/src/ecc_generate_key.rs | Defines FW TBOR wire schema + constants for EccGenerateKey (0x17). |
| fw/core/ddi/tbor/types/src/ecc_sign.rs | Defines FW TBOR wire schema + constants for EccSign (0x18). |
| fw/core/ddi/tbor/types/src/ecdh_derive.rs | Defines FW TBOR wire schema + constants for EcdhDerive (0x19). |
| docs/tbor-ddi/README.md | Adds the three new commands to the TBOR command table. |
| docs/tbor-ddi/commands/ecc_generate_key.md | Documents EccGenerateKey behavior, wire layout, and errors. |
| docs/tbor-ddi/commands/ecc_sign.md | Documents EccSign behavior, wire layout, and errors. |
| docs/tbor-ddi/commands/ecdh_derive.md | Documents EcdhDerive behavior, wire layout, and errors. |
| ddi/tbor/types/tests/commands/mod.rs | Registers new emulator integration test modules for the new commands. |
| ddi/tbor/types/tests/commands/ecc_generate_key.rs | Adds emu integration tests for keygen across curves/scopes and rejects. |
| ddi/tbor/types/tests/commands/ecc_sign.rs | Adds emu integration tests for sign across curves + unwrap→sign interoperability and rejects. |
| ddi/tbor/types/tests/commands/ecdh_derive.rs | Adds emu integration tests for ECDH derive across curves/scopes and rejects. |
| ddi/tbor/types/src/lib.rs | Registers the host-side TBOR request/response wrappers for the new commands. |
| ddi/tbor/types/src/ecc_generate_key.rs | Adds host-side TBOR wrapper types/constants for EccGenerateKey. |
| ddi/tbor/types/src/ecc_sign.rs | Adds host-side TBOR wrapper types/constants for EccSign. |
| ddi/tbor/types/src/ecdh_derive.rs | Adds host-side TBOR wrapper types/constants for EcdhDerive. |
9ac2c4f to
71a1bba
Compare
71a1bba to
fe7bd92
Compare
|
Hardened the pre-existing RSA OAEP test Why it failed on Windows: the test flips the most-significant ciphertext byte ( Fix: accept either rejection code, since both mean the corruption was correctly detected. |
fe7bd92 to
c80e592
Compare
c80e592 to
d56870e
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 23 out of 23 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
fw/core/ddi/tbor/types/src/ecc_sign.rs:124
- The
#[tbor(buffer, min_len = 164, max_len = 200)]literals formasked_keyare not pinned to the sharedMASKED_ECC_KEY_{MIN,MAX}_LENconstants. This repo typically addsconst _: () = assert!(...)tripwires so changes to the constants can’t silently desync the required integer literals in the TBOR derive (see the pattern infw/core/ddi/tbor/types/src/evidence.rs).
#[test]
fn lengths_match_pinned_values() {
const _: () = assert!(64 == ECC_DIGEST_MAX_LEN);
const _: () = assert!(136 == ECC_SIG_MAX_LEN);
assert_eq!(ECC_DIGEST_MAX_LEN, 64);
fw/core/ddi/tbor/types/src/ecdh_derive.rs:136
- The
#[tbor(buffer, min_len = 164, max_len = 200)]literals formasked_keyaren’t pinned toMASKED_ECC_KEY_{MIN,MAX}_LEN, so future changes to the shared masked-ECC envelope sizes could desync the required TBOR derive literals. Other TBOR schemas in this repo addconst _: () = assert!(...)tripwires for this (e.g.,fw/core/ddi/tbor/types/src/evidence.rs).
#[test]
fn lengths_match_pinned_values() {
const _: () = assert!(136 == ECDH_PEER_PUB_MAX_LEN);
const _: () = assert!(198 == MASKED_SECRET_MAX_LEN);
assert_eq!(MASKED_SECRET_MIN_LEN, 164);
Add three stateless masked-key TBOR crypto commands mirroring the MBOR vault-key-handle equivalents: - EccGenerateKey (0x17): generate an ECC keypair (P-256/384/521) and return the private key AEAD-GCM-256 masked under the requested scope plus the wire public key. Nothing is persisted on-device. - EccSign (0x18): unmask a caller-held masked ECC private key in place, ECDSA-sign a host-supplied pre-hashed digest, return raw r||s. - EcdhDerive (0x19): unmask a masked local ECC private key in place, derive an ECDH shared secret against a host peer public key, and re-mask the secret under a target scope. Uses the zero-copy reserve+fill encoder and in-place unmask (decode_mut) patterns. Recovered plaintext keys and derived secrets are scrubbed on every return path (scope exit only resets the bump watermark). ECC import is provided by the existing UnwrapKey (Ecc class); a cross-command test covers UnwrapKey-Ecc -> EccSign. Shared curve/kind mappings added to tbor from_pal (ecc_private, ecdh_secret, ecc_private_curve); dispatch/classifier wiring in mod.rs and op.rs. Wire schemas (fw + host mirror), emu integration tests (host- verified ECDSA roundtrip for all curves), and per-command docs included. Also harden the pre-existing RSA OAEP test decrypt_rejects_corrupted_oaep_ciphertext: corrupting the most-significant ciphertext byte can push the integer past the modulus, which OpenSSL reports as INTERNAL_ERROR but CNG (Windows) reports as DDI_CMD_FAILURE. Accept either rejection code so the platform-flaky test passes on Windows CI. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0b09e50a-a9be-4bae-b347-d42dc775a258
d56870e to
97c3a91
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 23 out of 23 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
ddi/tbor/types/tests/commands/ecdh_derive.rs:154
- The EcdhDerive integration tests cover curve/scopes and bad peer length, but they don’t exercise the handler’s key-class confusion guard (it rejects non-ECC-private masked blobs via
ecc_private_curve(..)before use). There’s an established pattern for this inrsa_mod_exp_wrong_key_class_rejected_emu; adding an analogous rejection test here would prevent regressions inInvalidKeyTypehandling.
#[test]
fn ecdh_derive_bad_peer_pub_len_rejected_emu() {
let ctx = TestCtx::new();
let session = finalized_co_session(&ctx);
let (masked_a, pub_b) = {
ddi/tbor/types/tests/commands/ecc_sign.rs:232
- The EccSign integration tests validate digest length rejections, but they don’t cover the key-class confusion guard in the handler (
ecc_private_curve(..)should reject non-ECC-private masked blobs withInvalidKeyType). Adding an explicit rejection test (similar torsa_mod_exp_wrong_key_class_rejected_emu) would help prevent regressions in key-kind validation.
#[test]
fn ecc_sign_digest_longer_than_curve_field_rejected_emu() {
let ctx = TestCtx::new();
let session = finalized_co_session(&ctx);
let (masked_key, _pub_key) = generate(&ctx, session.session_id, ECC_CURVE_P256);
Summary
Adds three stateless masked-key TBOR crypto DDI commands, mirroring the existing MBOR vault-key-handle equivalents. The private key is AEAD-GCM-256 masked and returned to the host (nothing persisted on-device); the caller passes the masked blob back on use.
0x17EccGenerateKey0x18EccSignr‖sover a host digest0x19EcdhDeriveECC import is already provided by the existing
UnwrapKey(Ecc class) from the base PR; a cross-command test coversUnwrapKey-Ecc →EccSign.Design (mirrors the sibling TBOR handlers)
decode_mutto unmask the masked key in place (no scratch copy) and reserve+fill outputs.EccSign/EcdhDeriverecover the curve via a strictfrom_pal::ecc_private_curvethat rejects non-ECC-private kinds (incl.SdSealing); usage attrs (sign/derive) enforced post-unmask.Changes
fw/core/lib/src/ddi/tbor/{ecc_generate_key,ecc_sign,ecdh_derive}.rs.fw/core/lib/src/ddi/tbor/from_pal.rs(ecc_private,ecdh_secret,ecc_private_curve).mod.rs(opcodes, dispatch, 3 classifiers),op.rs(from_tbor_opcode).lib.rsregistration.UnwrapKey-Ecc→sign cross-test + rejects; ecdh all curves/scopes + reject..md+ README rows.Testing
cargo checkfw core (host + Uno no_std) ✅Notes
tbor/unwrap-key); uses opcodes0x17–0x19to avoid collision with the sibling AES/HMAC branches.Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com